package gameclasses;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;

import datastructures.Vector2f;

import exceptions.Exception;

import interfaces.IUnit;
import interfaces.IObserver;

public class ConsoleGame implements IObserver {
	
	boolean killgame = false;
	private Player p;
	private GameManager gm;
	private	BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	
	/**
	 * starts the {@link ConsoleGame} with a {@link GameManager} and the startmenu
	 * @param gm gamemanager the {@link ConsoleGame} uses
	 */
	public ConsoleGame(GameManager gm) {
		this.gm = gm;
		p = gm.getActivePlayer();
		menu();
	}
	
	/**
	 * Displays the GameMenu in which the player can select his/her actions
	 * and waits for his/her input to react in the right way
	 */
    private void menu() {		
		char eingabe;		
		Hilfe();
		
		while (!killgame)
		{
			System.out.println("Warte auf Eingabe...    (druecke H fuer Hilfe)");
			try{
				eingabe = in.readLine().charAt(0);
			}
			catch(IOException e)
			{
				System.out.println("Eingabe konnte nicht interpretiert werden!");
				eingabe = 'x';
			}
			System.out.println("[Spieler" + gm.getActivePlayerID()+"]:");	
			switch (eingabe) {
			
			case 'p':
			case 'P':
				PanzerKauf();
				break;
				
			case 'S':
			case 's':
				Selektieren();
				break;

			case 'B':
			case 'b':				
				BewegenBallern();
				break;

			case 'N':
			case 'n':
				NextPlayer();	
				break;

			case 'Q':
			case 'q':
				Quit();
				break;
				
			case 'E':
			case 'e':
				Einheitenliste();
				break;
				
			case 'm':
			case 'M':
				printMap();
				break;
				
			case 'h':
			case 'H':
				Hilfe();
				break;
				
			case 'l':
			case 'L':
				Log();
				break;
			case 'R':
			case 'r':
				ressourcen();
				break;
				
			case 'd':
			case 'D':
				debugshit();
				break;
				
			default:
				System.out.println("Befehl nicht erkannt... Druecken sie H fuer Hilfe");
			}

		}

	}

	
	
	private void debugshit() {
		//diverse tests und �hnliches
		try {
			gm.getMap().getRoute(new Vector2f(10,10), new Vector2f(20,20), IUnit.TYPE_LAND);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Buys a Unit and displays it
	 */
	private void PanzerKauf(){
		int errcode = gm.buyUnit();
		if (errcode>=0) {
			System.out.println("Panzer erfolgreich gekauft. Verbleibendes Geld: " + p.getMoney());
		} 
		else{
			System.out.println(Exception.interpretErrorcode(errcode));
		}
	}
	
	/**
	 * Selects a Unit on a specified {@link Point} and displays it
	 * else shows an errormessage
	 */
	private void Selektieren(){
		Vector2f ort = readPoint();
		if (gm.selectUnit(ort)==0) {
		
			System.out.println("Einheit auf " + printPoint(ort) + " selektiert.");
		} 
		else {
			System.out.println("Keine Einheit zum selektieren auf " + printPoint(ort));
		}
	}
	
	/**
	 * Takes a {@link Point} from the player and tells the selected Unit to
	 * either move or shot to this point
	 */
	private void BewegenBallern(){
		Vector2f ortt = readPoint();
		int temp = gm.unitAction(ortt);
		System.out.println(Exception.interpretErrorcode(temp));
	}
	
	/**
	 * Ends the turn of the player and selects the next one
	 */
	private void NextPlayer(){
		gm.nextPlayer();
		System.out.println("Zug beendet. Spieler " +gm.getActivePlayerID() + " ist nun am Zug");
	}
	
	/**
	 * Quits the game by killing it
	 */
	private void Quit(){
		killgame = true;
		System.out.println("Spiel wird beendet....");
	}
	
	/**
	 * Displays all Units which the active player has 
	 */
	private void Einheitenliste(){
		Vector<IUnit> unitlist = gm.getUnitsOf(-1);
		System.out.println("Du hast folgende Einheiten:");
		for (IUnit u : unitlist)
		{
			System.out.println(u.getStatus());
		}
	}
	
	/**
	 * Displays the possible input options which the active player has
	 */
	private void Hilfe(){
		System.out.println("+------GAMEMENU------+ \n -(P)anzer kaufen \n -(S)elektieren \n -(B)ewegen / (B)allern! \n " +
				"-(N)aechster Spieler \n -(E)einheiten anzeigen \n -(M)ap anzeigen \n -(R)essourcen anzeigen" +
				" \n -(L)og anzeigen \n -(Q)uit");
	}
	
	/**
	 * Shows the whole Log of the currend game
	 */
	private void Log(){
		gm.getLogger().printAll();
	}
	
	/**
	 * Shows the current ressources of the active plaayer (Money and Points)
	 */
	private void ressourcen(){
		System.out.println("Aktuelles Geld: "+ p.getMoney());
		System.out.println("Aktuelle Punkte: " + p.getPoints());
	}
	
		
	/**
	 * Takes an x and y input of the player and transforms it to a {@link Vector2f}
	 * @return
	 */
	private Vector2f readPoint(){
		try {
			System.out.println("Bitte x-Koordinaten eingeben.");
			int x = Integer.parseInt(in.readLine());
			System.out.println("Bitte y-Koordinaten eingeben.");
			int y = Integer.parseInt(in.readLine());
			return new Vector2f(x, y);
		}
		catch(IOException e)
		{
			e.printStackTrace();
			return new Vector2f(0,0);
		}
		finally {}
	}
	
	/**
	 * Displays a {@link Vector2f} as x- and y-coordinate in the console
	 * @param p vector which will be converted
	 * @return  displays the {@link Vector2f} as a string
	 */
	private String printPoint(Vector2f p)
	{
		return "("+p.x()+"|"+p.y()+")";
	}
	
	/**
	 * Scanns through every coordinate of the Map and displays it as a chart-map
	 */
	private void printMap(){
		Map map = gm.getMap();
		Vector<IUnit> units = gm.getUnitsOf(-2);
		
		System.out.print("\n");
		
		for(int i=0;i<map.getWidth();i++)
		{
			for(int j=0;j<map.getHeight();j++)
			{
				boolean isunit = false;
				for(IUnit u:units)
				{
					if(u.getPosition().equals(new Vector2f(i,j)))
					{
						System.out.print(u.getPlayerID());
						isunit = true;
						break;
					}
				}
				if(!isunit)
				{
					switch(map.getData(i,j))
					{
						case Map.TERRAIN_GRASS: System.out.print(".");break;
						case Map.TERRAIN_WATER: System.out.print("~");break;
						case Map.TERRAIN_FORREST: System.out.print("A");break;
						case Map.TERRAIN_ROCK: System.out.print("X");break;
						case Map.TERRAIN_MUD: System.out.print(",");break;
						default: System.out.print("B");
					}
				}
				
			}
			System.out.println();
		}
		
	}
	
	@Override
	public void Update() {
		p = gm.getActivePlayer();
	}

}